module.exports = (function(){
  var components = {};
  function _Vue(args){
    var _this = this;
    this.id = args.id || a.zIndex();
    this.created = args.created || function(){};
    this.mounted = args.mounted || function(){};
    this.dom = null;
    this._dom = args._dom;
    this.refs = {};
    this.$slots = args.slots;
    this.parent = args.parent;
    this.domToVnodeTree = function(dom){
      var rc = {
        tagName:dom.tagName,
        nodeType:dom.nodeType,
        nodeValue:dom.nodeValue,
        attributes:{},
        childNodes:[]
      }
      if(dom.nodeType == 1){
        if(dom.attributes.length > 0) {
          for(var i = 0; i < dom.attributes.length; i ++) {
            rc.attributes[dom.attributes[i].name] = dom.attributes[i].value;
          }
        }
        for(var i = 0; i < dom.childNodes.length; i ++) {
          rc.childNodes.push(this.domToVnodeTree(dom.childNodes[i]));
        }
      }
      return rc;
    }
    if(typeof args.el == 'string'){
      if(args.el.charAt(0) == '#'){
        var id = args.el.replace('#','');
        this._dom = document.getElementById(id);
        this.dom = this.domToVnodeTree(this._dom);
      }
    }else{
      var dom = document.createElement('div');
      dom.innerHTML = args.template || '';
      var node = dom.firstChild;
      while(node){
        if(node.nodeType == 1){
          this.dom = this.domToVnodeTree(node);
          node = null;
        }else{
          node = node.nextSibling;
        }
      }
    }
    for(var key in args.data) this[key] = args.data[key];
    for(var key in args.methods) _this[key] = args.methods[key];
    this.buildScript = function(_script,o){
      var ogobal = '';
      for(var key in o) ogobal += ',' + key + '=o["' + key + '"]';
      return '(function(){\rvar _this=o' + ogobal + ';\r' + _script + ';\r}).call(o)';
    }
    this.parseString = function(s,o){
      s = s.replace(/{{/g,'{').replace(/}}/g,'}');
      s = s.replace(new RegExp('{([^}]+)}','gmi'),function($0,$1){
        var _VALUE_ = '';
        eval(_this.buildScript('_VALUE_ = ' + $1,o));
        return _VALUE_;
      });
      return s;
    }
    this.addEventListener = function(dom,name,fun,bool){
      bool = bool || false;
      if(dom.addEventListener){
        dom.addEventListener(name,function(event){
          fun.call(dom,event || window.event);
        },bool);
      }else if(dom.attachEvent){
        dom.attachEvent('on' + name,function(event){
          fun.call(dom,event || window.event);
        },bool);
      }
    }
    this.parseAttr = function(dom,o,key,prefix){
      prefix = prefix || ':';
      var attr = dom.getAttribute(prefix + key),_VALUE_ = '';
      if(attr) eval(this.buildScript('_VALUE_=' + attr,o));
      switch(key){
        case 'style':
          if(typeof _VALUE_ == 'string'){
            dom.attributes['style'] = _VALUE_;
          }else if(typeof _VALUE_ == 'object' && _VALUE_){
            var styles = '';
            for(var k in _VALUE_){
              styles += k.replace(/([A-Z])/,function($0,$1){
                return '-' + $1.toLowerCase($1);
              })
              styles += ':' + _VALUE_[k] + ';';
            }
            dom.attributes['style'] = styles;
          }
          break;
        case 'class':
          var classes  = _VALUE_;
          if(typeof _VALUE_ == 'object' && _VALUE_){
            classes  = '';
            for(var k in _VALUE_) if(_VALUE_[k]) classes += ' ' + k;
          }
          dom.attributes['class'] = classes.replace(' ','');
          break;
        case 'model':
          if(dom.tagName == 'INPUT' || dom.tagName == 'textarea'){
            dom.value = _VALUE_ == undefined ? '' : _VALUE_;
            attr = dom.getAttribute(prefix + key);
            dom.addEventListener('change',function(){
              if(this.type == 'password'){
                eval(_this.buildScript('_this.' + attr + '="' + this.value + '"'));
              }else{
                var obj = {};
                eval(_this.buildScript('obj.' + attr + '="' + this.value + '"'));
                _this.setData(obj);
              }
            })
          }
          break;
        default:
          if(dom.tagName == 'INPUT' || dom.tagName == 'textarea'){
            if(key == 'disabled' && !_VALUE_) {
              break;
            }else if(key == 'maxlength' && !/^\d+$/.test(_VALUE_)){
              break;
            }
          }
          dom.setAttribute(key,_VALUE_);
          break;
      }
      dom.removeAttribute(prefix + key);
    }
    this.parseMethod = function(dom,o,key){
      var method = dom.getAttribute('@' + key);
      if(method) {
        method = method.split('(');
        dom.addEventListener(key,function(event){
          event = event || window.event;
          var args = [event];
          if(method[1] && /\)$/.test(method[1])) {
            args = method[1].replace(/\)$/,'').split(',');
            for(var i = 0; i < args.length; i ++){
              if(args[i] == '$event') {
                args[i] = event;
              }else{
                eval(_this.buildScript('args[i] = ' + args[i],o));
              }
            }
          }
          if(method[0] && typeof _this[method[0]] == 'function') _this[method[0]].apply(_this,args);
        })
      }
      dom.removeAttribute('@' + key);
    }
    this.parseElem = function(dom,o,attrs){
      for(var key in dom.attributes){
        switch(key.charAt(0)){
          case ':':
            this.parseAttr(dom,o,key.replace(':',''));
            break;
          case '@':
            this.parseMethod(dom,o,key.replace('@',''));
            break;
          default:
            if(dom.tagName == 'INPUT' && key == 'v-model') this.parseAttr(dom,o,key.replace('v-',''),'v-');
            break;
        }
      }
    }
    this.mounting = function(dom,o){
      var inIf = false;
      var node = dom.firstChild;
      var _ids = {},_id = dom._id || this.id;
      while(node){
        if(node.nodeType == 3 && /\S/.test(node.nodeValue)){
          node.nodeValue = this.parseString(node.nodeValue,o);
          inIf = false;
          if(!_ids[node.tagName]) _ids[node.tagName] = 0;
          node._id = _id + 'text' + _ids['text'];
          _ids['text'] ++;
          node = node.nextSibling;
          //node = node.previousSibling;
        }else if(node.nodeType == 1){
          if(!_ids[node.tagName]) _ids[node.tagName] = 0;
          node._id = _id + node.tagName + _ids[node.tagName];
          _ids[node.tagName] ++;
          var vif = node.getAttribute('v-if'),velif = node.getAttribute('v-else-if'),velse = node.getAttribute('v-else');
          velse = velse == undefined ? false : true;
          var vfor = node.getAttribute('v-for');
          var _VALUE_ = false;
          if(vif){
            node.removeAttribute('v-if');
            eval(this.buildScript('_VALUE_ = ' + vif,o));
            if(!_VALUE_) inIf = true;
          }else if(velif){
            node.removeAttribute('v-else-if');
            if(inIf) {
              eval(this.buildScript('_VALUE_ = ' + velif,o));
              if(_VALUE_) inIf = false;
            }
          }else if(velse){
            node.removeAttribute('v-else');
            if(inIf) _VALUE_ = true;
            inIf = false;
          }else{
            _VALUE_ = true;
            inIf = false;
          }
          if(vfor) {
            node.removeAttribute('v-for');
            vfor = vfor.split(' in ');
            eval(this.buildScript('vfor[1] = ' + vfor[1],o));
            if(vfor[1].length == 0) _VALUE_ = false;
          }
          if(!_VALUE_) {
            var _node = node;
            node = node.nextSibling;
            dom.removeChild(_node);
          }else{
            if(vfor){
              var items = vfor[1],itemNames = vfor[0].replace('(','').replace(')','').split(',');
              var itemName = itemNames[0],indexName = itemNames[1] || 'index';
              var index = 0,cloneNode;
              for(var key in items){
                if(key == 'forEach' && items instanceof Array) continue;
                cloneNode = node.cloneNode(true);
                cloneNode._id = node._id;
                node._id = _id + node.tagName + _ids[node.tagName];
                _ids[node.tagName] ++;
                dom.insertBefore(cloneNode,node);
                o[indexName] = index;
                o[itemName] = items[key];
                this.mounting(cloneNode,o);
                cloneNode = this.component(cloneNode,o);
                index ++;
              }
              dom.removeChild(node);
              node = cloneNode.nextSibling;
              //node = cloneNode.previousSibling;
              
            }else{
              this.mounting(node,o);
              node = this.component(node,o);
              node = node.nextSibling;
              //node = node.previousSibling;
            }
          }
        }else{
          node = node.nextSibling;
          //node = node.previousSibling;
        }
      }
    }
    this.$emit = function(name,_args){
      if(name && args.$emits[name]) args.$emits[name](_args);
    }
    this.slots = {};
    this.component = function(dom,o){
      var tagName = dom.tagName.toUpperCase();
      var attrs = dom.attributes;
      if(tagName == 'SLOT'){
        this.slots[dom.getAttribute('name') || '_default'] = dom;
        return dom;
      } else if(components[tagName]){
        this.refs[dom._id] = dom;
        return dom;
      }else{
        this.parseElem(dom,o,attrs);
        return dom;
      }
    }
    this.parseComponent = function(refName){
      var vdom = this.refs[refName],attrs = vdom.attributes;
      var component = a.clone(components[vdom.tagName]);
      component._dom = vdom.realdom;
      component.data = component.data();
      component.$emits = {};
      var props = {};
      if(component.props instanceof Array) {
        for(var i = 0; i < component.props.length; i++) {
          props[component.props[i]] = {type: Object};
          props[component.props[i]]['value'] = '';
        }
      }else{
        for(var key in component.props) {
          props[key] = component.props[key];
          if(props[key]['default'] != undefined) props[key]['value'] = props[key]['default'];
        }
      }
      for(var key in attrs){
        (function(i){
          var attrName = attrs[i],_VALUE_ = vdom.getAttribute(attrs[i]);
          switch(attrs[i].charAt(0)){
            case ':':
              attrName = attrs[i].replace(':','').replace(new RegExp('-([a-z])'),function($0,$1){return $1.toUpperCase()});
              eval(_this.buildScript('_VALUE_ = ' + _VALUE_,o));
              if(props[attrName]) props[attrName].value = _VALUE_;
              break;
            case '@':
              attrName = attrs[i].replace('@','').replace(new RegExp('-([a-z])'),function($0,$1){return $1.toUpperCase()});
              component.$emits[attrName] = function(args){
                _this[_VALUE_](args);
              }
              break;
            default:
              if(props[attrName]) props[attrName].value = _VALUE_;
              break;
          }
          if(attrName == 'ref') refName = _VALUE_;
        })(key)
      }
      var propData;
      for(var key in props) {
        if(!propData) propData = {};
        propData[key] = props[key].value;
        component.data[key] = props[key].value;
      }

      var node = vdom.firstChild,slots = {};
      while(node){
        var slotName = '_default';
        if(node.nodeType == 1) slotName = node.getAttribute('slot') || '_default';
        if(!slots[slotName]) slots[slotName] = [];
        slots[slotName].push(node);
        node = node.nextSibling;
      }
      component.slots = slots;
      if(this.refs[refName] instanceof _Vue){
        this.refs[refName].$emits = component.$emits;
        this.refs[refName].slots = slots;
        this.refs[refName].setData(propData);
      }else{
        component.parent = this;
        this.refs[refName] = new _Vue(component);
      }
    }
    this.prevVdom = null;
    this.currentVdom = null;
    this.createElement = function(vnode){
      var node = null;
      if(vnode.nodeType == 1) {
        node = document.createElement(vnode.tagName);
        node.vnode = vnode;
        vnode.realdom = node;
        for(var key in vnode.attributes){
          switch(key){
            case 'class':
              node.className = vnode.attributes[key];
              break;
            case 'id':
              node.id = vnode.attributes[key];
              break;
            default:
              node.setAttribute(key,vnode.attributes[key]);
              break;
          }
        }
        for(var eventType in vnode.events){
          for(var funName in vnode.events[eventType]){
            var fun = vnode.events[eventType][funName];
            this.addEventListener(node,eventType,fun.func,fun.useCapture);
          }
        }
      }else if(vnode.nodeType == 3){
        node = document.createTextNode(vnode.nodeValue);
      }
      /*if(node){
        node.vnode = vnode;
        vnode.realdom = node;
      }*/
      return node;
    }
    this.vnodeToDom = function(vnode){
      var node = this.createElement(vnode);
      if(vnode.nodeType == 1 && vnode.childNodes.length > 0){
        for(var i = 0; i < vnode.childNodes.length; i ++){
          var childi = this.vnodeToDom(vnode.childNodes[i]);
          if(childi) node.appendChild(childi);
        }
      }
      return node;
    }
    this.mergeVnode = function(newVnode,oldVnode){
      var domNode = this.vnodeToDom(newVnode);
      this._dom.parentNode.replaceChild(domNode,this._dom);
      this._dom = domNode;
    }
    this.setData = function(data){
      if(data) a.merge(this,data);
      this.currentVdom = a.Vnode.parseVnodeTree(this.dom);
      this.parseElem(this.currentVdom,this);
      this.mounting(this.currentVdom,this);
      if(!this.prevVdom) {
        var domNode = this.vnodeToDom(this.currentVdom);
        this._dom.parentNode.replaceChild(domNode,this._dom);
        this._dom = domNode;
      }else{
        this.mergeVnode(this.currentVdom,this.prevVdom);
      }
      this.prevVdom = this.currentVdom;
      for(var key in this.refs) this.parseComponent(key);
      //解析slot
      for(key in this.slots){
        if(!args.slots[key] && this.slots[key].realdom) continue;
        var slotsRealdom = this.slots[key].realdom,parent = slotsRealdom.parentNode;
        for(var i = 0; i < args.slots[key].length; i ++){
          slotsRealdom.parentNode.insertBefore(args.slots[key][i].realdom,slotsRealdom);
        }
        slotsRealdom.parentNode.removeChild(slotsRealdom);
      }
    }
    if(this.created) this.created.call(this);
    this.setData();
    if(this.mounted) this.mounted.call(this);
  }
  _Vue.component = function(tagName,args){
    if(args.template.charAt(0) === '#') args.template = document.getElementById(args.template.replace('#','')).innerHTML;
    components[tagName.toUpperCase()] = args;
  }
  return _Vue;
})()